package org.zebra.mybatis.plus.ext.datapermission.dept;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import org.zebra.mybatis.plus.ext.datapermission.DataPermissionRule;
import org.zebra.mybatis.plus.ext.datapermission.dept.annotation.DeptDataPermissionField;

import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 数据权限也是基于角色实现的
 *
 * @author zhanghongbin
 */
public abstract class DeptDataPermissionRule implements DataPermissionRule {

    @Override
    public Expression getExpression(Method method) {
        DeptDataPermissionField deptDataPermissionField =
                AnnotationUtil.getAnnotation(method, DeptDataPermissionField.class);
        if (deptDataPermissionField == null) return null;
        Expression expression = null;
        DataPermissionCurrentUser dataPermissionCurrentUser = this.getDataPermissionCurrentUser();
        String userId = dataPermissionCurrentUser.getUserId();
        String deptId = dataPermissionCurrentUser.getDeptId();
        Set<DataPermissionCurrentUser.Role> roleSet = dataPermissionCurrentUser.getRoles();
        for (DataPermissionCurrentUser.Role role : roleSet) {
            DataScopeType dataScopeType = DataScopeType.getDataScopeType(role.getDataScope());
            if (dataScopeType == null) continue;
            // 所有权限
            if (dataScopeType == DataScopeType.ALL) {
                return null;
            } else if (dataScopeType == DataScopeType.SELF) {
                Expression self = this.getSelfOrDept(
                        deptDataPermissionField.userIdFieldName(), deptDataPermissionField.userIdFieldType(), userId);
                expression = null != expression ? new OrExpression(expression, self) : self;
            } else if (dataScopeType == DataScopeType.DEPT) {
                Expression dept = this.getSelfOrDept(
                        deptDataPermissionField.deptIdFieldName(), deptDataPermissionField.deptIdFieldType(), deptId);
                expression = null != expression ? new OrExpression(expression, dept) : dept;
            } else if (dataScopeType == DataScopeType.DEPT_AND_CHILD) {
                InExpression inExpression = getDeptAndChildOrCustom(
                        deptDataPermissionField.deptIdFieldName(),
                        deptDataPermissionField.deptIdFieldType(),
                        this.getDeptAndChildId(deptId));
                expression = null != expression ? new OrExpression(expression, inExpression) : inExpression;
            } else if (dataScopeType == DataScopeType.CUSTOM) {
                InExpression inExpression = getDeptAndChildOrCustom(
                        deptDataPermissionField.deptIdFieldName(),
                        deptDataPermissionField.deptIdFieldType(),
                        this.getDeptIdByCustom(role.getRoleId()));
                expression = null != expression ? new OrExpression(expression, inExpression) : inExpression;
            }
        }
        return expression;
    }

    protected InExpression getDeptAndChildOrCustom(
            String columnName, DeptDataPermissionField.FieldType fieldType, Set<String> deptIdSet) {
        InExpression inExpression = new InExpression();
        inExpression.setLeftExpression(new Column(columnName));
        List itemList;
        if (fieldType == DeptDataPermissionField.FieldType.NUMBER) {
            itemList = convertList(deptIdSet, LongValue::new);
        } else {
            itemList = convertList(deptIdSet, StringValue::new);
        }
        inExpression.setRightItemsList(new ExpressionList(itemList));
        return inExpression;
    }

    private <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
        if (CollUtil.isEmpty(from)) {
            return new ArrayList<>();
        }
        return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList());
    }

    protected abstract Set<String> getDeptIdByCustom(String roleId);

    protected abstract Set<String> getDeptAndChildId(String deptId);

    /**
     * 获取当前用户权限部门信息
     *
     * @return 对象
     */
    protected abstract DataPermissionCurrentUser getDataPermissionCurrentUser();

    protected Expression getSelfOrDept(String columnName, DeptDataPermissionField.FieldType fieldType, String value) {
        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(new Column(columnName));
        if (fieldType == DeptDataPermissionField.FieldType.NUMBER) {
            equalsTo.setRightExpression(new LongValue(Long.parseLong(value)));
        } else if (fieldType == DeptDataPermissionField.FieldType.STRING) {
            equalsTo.setRightExpression(new StringValue(value));
        }
        return equalsTo;
    }

    /**
     * 数据权限类型
     *
     * @author zhanghongbin
     */
    @Getter
    @AllArgsConstructor
    enum DataScopeType {

        /**
         * 全部数据权限
         */
        ALL(1),

        /**
         * 部门数据权限
         */
        DEPT(2),

        /**
         * 部门及以下数据权限
         */
        DEPT_AND_CHILD(3),

        /**
         * 仅本人数据权限
         */
        SELF(4),

        /**
         * 自定数据权限
         */
        CUSTOM(5);

        private final Integer code;

        public static DataScopeType getDataScopeType(Integer code) {
            for (DataScopeType type : values()) {
                if (type.getCode() == code) {
                    return type;
                }
            }
            return null;
        }
    }
}
